<!-- eslint-disable vue/require-v-for-key -->
<template>
  <div class="container">
    <div class="trigger" ref="triggerRef" @click="trigger">
      <div
        class="cross"
        @drop.prevent="fileDropHandler"
        @dragover.prevent="dragOverHandler"
        @dragleave.prevent="dragLeaveHandler"
      >
        <div class="bar vertical"></div>
        <div class="bar horizontal"></div>
      </div>
    </div>

    <input
      ref="inputRef"
      type="file"
      @change="filePickHandler"
      style="display: none"
    />
  </div>

  <div class="tools">
    <n-button @click="abort">暂停</n-button>
  </div>

  <div class="container">
    <div class="preview">
      <n-progress
        type="multiple-circle"
        :stroke-width="6"
        :circle-gap="0.5"
        :percentage="[percentageHash, percentageUpload]"
        style="width: 100%"
      >
        <div style="text-align: center">{{ theFile?.name ?? "" }}</div>
      </n-progress>
    </div>
  </div>

  <div class="progress">
    <div
      v-for="p in progress"
      class="progress_item"
      :style="{
        '--p': p + '%',
      }"
    ></div>
  </div>
</template>

<script setup lang="ts">
import { queryChunks, mergeChunks } from "@/api/backend";
import { useMessage } from "@/hooks/message";
import { computed, ref } from "vue";
import HashWorker from "./HashWorker?worker";
import { queueUpload } from "./queueUpload";
// import { createMD5 } from "hash-wasm";
// import { IHasher } from "hash-wasm/dist/lib/WASMInterface";

export interface ZRadioGroupProps {
  value: any;
  size?: number;
  disabled?: boolean;
}
const props = withDefaults(defineProps<ZRadioGroupProps>(), {
  size: 180,
});
const sizePx = computed(() => props.size + "px");

// const emit = defineEmits<{
//   (e: "update:value", value: any): void;
// }>();
// const updateValue = (v: any) => {
//   emit("update:value", v);
// };

const { zMessage } = useMessage();

const theFile = ref<any | null>(null);
const percentageHash = ref<number>(0);
const percentageUpload = ref<number>(0);
const progress = ref<number[]>([]);
const currentController = ref<AbortController>();

const CHUNK_SIZE = 100 * 1024; // MB * KB
const IS_SAMPLE_HASH = false; // 抽样哈希

const triggerRef = ref();
const inputRef = ref();
function trigger() {
  inputRef.value.click();
}
async function filePickHandler(event: any) {
  if (event.target.files) {
    const file: File = event.target.files[0];
    console.log(file);
    theFile.value = file;

    process(file);
    inputRef.value.value = null;
  } else {
    zMessage.warning("什么都没选中哦");
  }
}

function fileDropHandler(e: DragEvent) {
  if (e.dataTransfer?.files) {
    console.log(e.dataTransfer.files);
    const file: File = e.dataTransfer.files[0];
    theFile.value = file;
    process(file);
    inputRef.value.value = null;
  } else {
    zMessage.warning("什么都没选中哦");
  }
}

function dragLeaveHandler() {
  triggerRef.value.style.border = "1px dashed #ccc";
}
function dragOverHandler() {
  triggerRef.value.style.border = "1px solid #a00";
}

async function process(file: File) {
  // 1. chunk
  const chunks: any[] = [];
  let curIndex = 0;
  let curBit = 0;
  while (curBit < file.size) {
    const endBit = Math.min(curBit + CHUNK_SIZE, file.size);
    chunks[curIndex] = {
      blob: file.slice(curBit, endBit),
      name: [file.name, curBit].join("_"),
    };
    curIndex++;
    curBit = endBit;
  }

  console.log(chunks);

  // 2. hash
  /* -- worker -- */
  const worker = new HashWorker();
  worker.onmessage = (e) => {
    if (e.data.done) {
      console.log(e.data.hash);
      const hash = e.data.hash;
      worker.terminate();
      percentageHash.value = 100;
      console.timeEnd("hash");
      upload(chunks, hash, file.name);
    } else {
      console.log(e.data.progress);
      percentageHash.value = Math.floor(
        (e.data.progress.split("_").slice(-1)[0] / file.size) * 100
      );
      loadChunk();
    }
  };
  curIndex = 0;
  // eslint-disable-next-line no-inner-declarations
  function loadChunk() {
    if (curIndex < chunks.length) {
      worker.postMessage(chunks[curIndex]);
      curIndex++;
      if (IS_SAMPLE_HASH) {
        curIndex++;
      }
    } else {
      worker.postMessage({ done: true });
    }
  }
  console.time("hash");
  loadChunk();

  /* -- rcb -- */

  // let md5 = await createMD5();
  // md5.init();
  // curIndex = 0;
  // // eslint-disable-next-line no-inner-declarations
  // async function loadChunk() {
  //   if (curIndex < chunks.length) {
  //     md5.update(new Uint8Array(await chunks[curIndex].blob.arrayBuffer()));
  //     curIndex++;
  //     if (IS_SAMPLE_HASH) {
  //       curIndex++;
  //     }
  //     percentageHash.value = Math.floor((curIndex / chunks.length) * 100);
  //     requestIdleCallback((deadline) => {
  //       if (deadline.timeRemaining() > 1) {
  //         loadChunk();
  //       }
  //     });
  //   } else {
  //     const hash = md5.digest();
  //     console.log(hash);
  //     console.timeEnd("hash");
  //     percentageHash.value = 100;
  //     upload(chunks, hash, file.name);
  //   }
  // }
  // console.time("hash");
  // loadChunk();
}

async function upload(chunks: any[], hash: string, fileName: string) {
  const uploadedChunks = await queryChunks({ hash, fileName: fileName });
  if (!uploadedChunks) return; // 文件已经存在了

  const toUploadChunks = chunks.filter(
    (ck) => !uploadedChunks.includes(ck.name)
  );

  /* -- Promise.all -- */
  // await Promise.all(
  //   toUploadChunks.map((ck) => {
  //     const fd = new FormData();
  //     fd.append("file", ck.blob);
  //     fd.append("hash", hash);
  //     fd.append("chunkName", ck.name);
  //     return uploadChunks(fd);
  //   })
  // );

  function onUploadProgress(p: number) {
    console.log(p);
    percentageUpload.value = p * 100;
  }

  function onChunkProgress(pro: number[]) {
    const a = Array(chunks.length).fill(0);
    for (let i = 0; i < chunks.length; i++) {
      a[i] =
        i < uploadedChunks.length
          ? 100
          : Math.floor((pro[i - uploadedChunks.length] ?? 0) * 100);
    }
    progress.value = a;
  }

  currentController.value = new AbortController();
  queueUpload(
    toUploadChunks.map((ck) => {
      const fd = new FormData();
      fd.append("file", ck.blob);
      fd.append("hash", hash);
      fd.append("chunkName", ck.name);
      return fd;
    }),
    5,
    onUploadProgress,
    onChunkProgress,
    currentController.value.signal
  )
    .then(async () => {
      const res = await mergeChunks({ hash, fileName: fileName });
      dragLeaveHandler();
      console.log(res);
    })
    .catch((e) => {
      zMessage.warning(e);
    });
}

function abort() {
  currentController.value?.abort();
}
</script>

<style scoped lang="scss">
.container {
  width: v-bind(sizePx);
  height: v-bind(sizePx);
  border: 1px dashed #ccc;

  .trigger {
    --bar-width: 20px;

    width: 100%;
    height: 100%;

    position: relative;
    cursor: pointer;
    .bar {
      height: var(--bar-width);
      width: 80%;
      background-color: #ccc;
      position: absolute;
      top: calc(50% - var(--bar-width) / 2);
      left: 10%;
    }
    .vertical {
      transform: rotate(90deg);
    }
  }
}

.tools {
  margin: 4px 0;
}

.progress {
  display: flex;
  flex-wrap: wrap;
  border: 1px dashed #ccc;

  .progress_item {
    margin: 1px;
    background-image: linear-gradient(
      180deg,
      dodgerblue 0%,
      dodgerblue var(--p),
      white var(--p),
      white 100%
    );
    width: 24px;
    height: 24px;
    border: 1px dashed #ccc;
  }
}
</style>
